package com.cupdata.zicon.jdk7concurrent.chapter4;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;

/**
 * In this recipe, you have learned how to send a list of tasks to an executor
 * and wait for the finalization of all of them using the invokeAll() method.
 * This method receives a list of the Callable objects and returns a list of the
 * Future objects. This list will have a Future object per task in the list. The
 * first object in the list of the Future objects will be the object that
 * controls the first task in the list of the Callable objects, and so on. 
 * 
 * The
 * first point to take into consideration is that the type of data used for the
 * parameterization of the Future interface in the declaration of the list that
 * stores the result objects must be compatible with the one used to
 * parameterized the Callable objects. In this case, you have used the same type
 * of data: the Result class. 
 * 
 * Another important point about the invokeAll()
 * method is that you will use the Future objects only to get the results of the
 * tasks. As the method finishes when all the tasks have finished, if you call
 * the isDone() method of the Future objects that is returned, all the calls
 * will return the true value.
 * 
 * @author SunYabing
 *
 */
public class RunMulTaskAndProcessAllResults {

	public RunMulTaskAndProcessAllResults() {

	}

	public static void main(String[] args) {
		ExecutorService executor = (ExecutorService) Executors
				.newCachedThreadPool();
		List<Task> taskList = new ArrayList<>();
		for (int i = 0; i < 3; i++) {
			Task task = new Task(i + "_name");
			taskList.add(task);
		}

		List<Future<Result>> resultList = null;

		try {
			resultList = executor.invokeAll(taskList);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}

		executor.shutdown();

		System.out.println("Main: Printing the results");
		for (int i = 0; i < resultList.size(); i++) {
			Future<Result> future = resultList.get(i);
			try {
				Result result = future.get();
				System.out.println(result.getName() + ": " + result.getValue());
			} catch (InterruptedException | ExecutionException e) {
				e.printStackTrace();
			}

		}

	}
}

class Result {
	private String name;
	private int value;

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public int getValue() {
		return value;
	}

	public void setValue(int value) {
		this.value = value;
	}

}

class Task implements Callable<Result> {
	private String name;

	public Task(String name) {
		this.name = name;
	}

	@Override
	public Result call() throws Exception {
		System.out.printf("%s: Staring\n", this.name);
		try {
			long duration = (long) (Math.random() * 10);
			System.out.printf("%s: Waiting %d seconds for results.\n",
					this.name, duration);
			TimeUnit.SECONDS.sleep(duration);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}

		int value = 0;
		for (int i = 0; i < 5; i++) {
			value += (int) (Math.random() * 100);
		}

		Result result = new Result();
		result.setName(this.name);
		result.setValue(value);

		System.out.println(this.name + ": Ends");
		return result;
	}

}
